<?php
declare (strict_types=1);

namespace app\api\service\passport;

use app\api\model\User as UserModel;
use app\common\model\Setting as SettingModel;
use app\common\enum\Setting as SettingEnum;
use app\api\validate\passport\Login as ValidateLogin;
use app\api\validate\passport\LoginPassword as ValidateLoginPassword;
use app\api\validate\passport\LoginMpweixin as ValidateLoginMpweixin;
use app\common\library\wechat\WxUser;
use app\common\library\wechat\WXBizDataCrypt;
use app\common\library\wechat\ErrorCode;
use app\common\enum\Client as ClientEnum;
use app\api\model\UserOauth as UserOauthModel;
use tool\Captcha;

/**
 * 服务类：用户登录
 * Class Login
 * @package app\api\service\passport
 */
class Login
{
    /**
     * 用户信息 (登录成功后才记录)
     * @var UserModel|null $userInfo
     */
    private ?UserModel $userInfo;

    /**
     * 执行用户登录
     * @param array $data
     * @return bool
     * @throws BaseException
     * @throws \think\Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function login(array $data): bool
    {
        // 数据验证
        $this->validate($data);
        // 自动登录注册
        if(getPlatform() == ClientEnum::MP_WEIXIN){
            $this->registerwx($data);
        }else{
            $this->register($data);
        }
        return true;
    }

    /**
     * 当前登录的用户信息
     * @return UserModel
     */
    public function getUserInfo(): UserModel
    {
        return $this->userInfo;
    }

    /**
     * 自动登录注册
     * @param array $data
     * @throws BaseException
     * @throws \think\Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    private function register(array $data): void
    {
        // 查询用户是否已存在
        // 用户存在: 更新用户登录信息
        $userInfo = UserModel::detail(['mobile' => $data['mobile']]);
        if ($userInfo) {
            if(isset($data['repassword']) && $data['repassword'] != ''){
                throwError('该账号已注册');
            }
            if(isset($data['registerMethod']) && $data['registerMethod'] == 20 && !password_verify($data['password'], $userInfo['password'])){
                throwError('登录失败, 用户名或密码错误');
            }
            if(isset($data['registerMethod']) && $data['registerMethod'] == 10 && session('smsCaptcha') != $data['smsCode']){
                throwError('短信验证码错误');
            }
            $this->updateUser($userInfo);
            return;
        }
        // 用户不存在: 创建一个新用户
        if(!isset($data['repassword']) || $data['repassword'] != $data['password']){
            throwError('密码不一致');
        }
        $this->createUser($data['mobile'], encryption_hash($data['password']), '', isset($data['nick_name'])?$data['nick_name']:'');
    }

    private function registerwx(array $data): void
    {
        if(isset($data['registerMethod']) && $data['registerMethod'] == 10 && session('smsCaptcha') != $data['smsCode']){
            throwError('短信验证码错误');
        }
        // 查询用户是否已存在
        // 用户存在: 更新用户登录信息
        $userInfo = UserModel::detail(['mobile' => $data['mobile']]);
        if ($userInfo) {
            $this->updateUser($userInfo, $data['openid']);
            return;
        }
        // 用户不存在: 创建一个新用户
        $this->createUser($data['mobile'], '', $data['openid'], isset($data['nick_name'])?$data['nick_name']:'');
    }

    /**
     * 新增用户
     * @param string $mobile 手机号
     * @param bool $isParty 是否存在第三方用户信息
     * @param array $partyData 用户信息(第三方)
     * @return void
     * @throws \think\Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    private function createUser(string $mobile, string $password, string $openid='', string $nickname=''): void
    {
        // 用户信息
        $data = [
            'mobile' => $mobile,
            'password' => $password,
            'nick_name' => !empty($nickname) ? $nickname : (!empty($mobile) ? \hide_mobile($mobile) : ''),
            'platform' => \getPlatform(),
            'last_login_time' => \time()
        ];
        // 新增用户记录
        $model = new UserModel;
        $model->save($data);
        // 记录用户信息
        $this->userInfo = $model;
        if($openid != ''){
            $this->createUserOauth($model['user_id'], $openid);
        }
    }

    private function createUserOauth(int $userId, string $openid): void
    {
        $userOauth = new UserOauthModel;
        $userOauth->add([
            'user_id' => $userId,
            'oauth_id' => $openid,
            'oauth_type' => ClientEnum::MP_WEIXIN
        ]);
    }

     /**
     * 更新用户登录信息
     * @param UserModel $userInfo
     */
    private function updateUser(UserModel $userInfo, string $openid=''): void
    {
        // 用户信息
        $data = [
            'last_login_time' => \time()
        ];
        // 更新用户记录
        $userInfo->save($data);
        // 记录用户信息
        $this->userInfo = $userInfo;
        if($openid != ''){
            $userId = UserOauthModel::getUserIdByOauthId($openid, ClientEnum::MP_WEIXIN);
            if($userId == null){
                $this->createUserOauth($userInfo['user_id'], $openid);
            }
        }
    }

    /**
     * 数据验证
     * @param array $data
     * @return void
     * @throws BaseException
     */
    private function validate(array $data): void
    {
        $setting = SettingModel::getItem(SettingEnum::REGISTER);
        if(getPlatform() == ClientEnum::MP_WEIXIN){
            $validate = new ValidateLoginMpweixin;
        }else{
            // 数据验证
            if($setting['registerMethod'] == 10){
                $validate = new ValidateLogin;
            }else{
                $validate = new ValidateLoginPassword;
            }
        }
        if (!$validate->check($data)) {
            throwError($validate->getError());
        }
        if($data['password']!='' && $data['smsCode']==''){
            $captcha = new Captcha;
            $captcha->check($data['captchaCode'], $data['mobile']) OR throwError('图形验证码错误');
        }
    }

    /**
     * 微信小程序通过code获取session (openid session_key unionid)
     * @param string $code
     * @return array|false
     * @throws BaseException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public static function wxCode2Session(string $code)
    {
        // 获取当前小程序信息
        $config = $setting = SettingModel::getItem(SettingEnum::WXAPP);
        // 微信登录 (获取session_key)
        $WxUser = new WxUser($config['appid'], $config['appsecret']);
        $result = $WxUser->jscode2session($code);
        empty($result) && throwError($WxUser->getError());
        return $result;
    }

    /**
     * 解密微信的加密数据encryptedData
     * @param string $encryptedData
     * @param string $iv
     * @param string|null $sessionKey
     * @return mixed
     * @throws BaseException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \cores\exception\BaseException
     */
    public static function wxDecryptData(string $encryptedData, string $iv, ?string $sessionKey = null)
    {
        // 微信数据解密
        $WXBizDataCrypt = new WXBizDataCrypt($sessionKey);
        $plainData = null;
        $code = $WXBizDataCrypt->decryptData($encryptedData, $iv, $plainData);
        if ($code !== ErrorCode::$OK) {
            throwError('微信数据 encryptedData 解密失败');
        }
        return $plainData;
    }

    public function wxCode2Phone(string $code)
    {
        // 获取当前小程序信息
        $config = $setting = SettingModel::getItem(SettingEnum::WXAPP);
        // 微信登录 (获取session_key)
        $WxUser = new WxUser($config['appid'], $config['appsecret']);
        $result = $WxUser->code2phone($code);
        return $result;
    }
}